package com.hyg.common.utils;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.hyg.common.CommonLibary;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Author: huangyuguang
 * Date: 2022/5/5
 * Description: 本地日志管理
 * LogManager.addLog(TAG,msg); -- 保存日志到缓存
 * LogManager.save(); -- 保存日志到sd卡并清空缓存，http请求、socket连接报错时和退出程序时要调佣该方法保存一下
 *                   -- 此外，缓存够100条自动保存一次，每隔一分钟也可以保存一次，闪退时也会保存一次
 */
public class LogManager {
    private static LogManager instance;

    private Context mContext;
    private boolean isSaveLog = false;
    public String logPath;
    private String logFileName = "api-%s-%s.log";
    private List<String> logList = new ArrayList<>();
    private boolean timerIsStart = false;
    private String apiFileName;

    /**
     * 一分钟保存一次log
     *
     * 结果测试发现一旦延时任务比较长，就无法执行了。
     * 原来Handler的延迟是基于SystemClock.uptimeMillis()计算的。
     * 而SystemClock.uptimeMillis()则是活跃时间，如果设备休眠了，就不算时间了，延时执行自然就继续往后延了。
     *
     * 替代方案：
     * AlarmManager，可以运行在app的生命周期之外。一旦设置AlarmManager，它将一直调用，即使你的应用程序是不运行，甚至当你的设备是在睡眠，
     * 这会消耗更多的电池。因此，你必须小心那些执行频繁的定时任务。定时任务应该尽可能地设置低的频率。
     *
     * 休眠时app的子线程、handle都暂停，所以此处休眠时日志没必要监听
     */
    private final long HEART_BEAT_RATE = 60 * 1000;
    private Handler heartBeatHandler ;
    private Runnable heartBeatRunnable;

    public static LogManager getInstance(){
        if(instance == null){
            synchronized (LogManager.class){
                if(instance == null){
                    instance = new LogManager();
                }
            }
        }
        return instance;
    }

    public LogManager init(Context context) {
        return init(context,getLogPath(context));
    }

    /**
     * 在Application初始化
     */
    public LogManager init(Context context, String logPath) {
        this.mContext = context.getApplicationContext();
        this.logPath = logPath;
        FileUtils.deleteFilesOlderThanNDays(logPath,3);
        isSaveLog = true;
        heartBeatHandler = new Handler();
        heartBeatRunnable = new Runnable() {
            @Override
            public void run() {
                savaLogs();
                //每隔一定的时间，对长连接进行一次心跳检测
                heartBeatHandler.postDelayed(this, HEART_BEAT_RATE);
            }
        };
        return this;
    }

    public String getTodayDate(){
        DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        return formatter.format(new Date());
    }

    private String getLogPath(Context context){
        if(logPath != null && logPath.length() > 7) return logPath;
        return context.getFilesDir().getPath() + "/logs/"; //API 和 socket日志
    }

    public synchronized void addLog(@Nullable String tag, @NonNull String msg){
        if(!isSaveLog || TextUtils.isEmpty(msg)) return;
        String logStr;
        if(TextUtils.isEmpty(tag)){
            logStr = String.format("%s %s\r\n", DateUtils.getCurrentDate(),msg);
        }else {
            logStr = String.format("%s %s:: %s\r\n", DateUtils.getCurrentDate(),tag,msg);
        }
        logList.add(logStr);
        if(logList.size() > 100){
            startHeartBeat();
            savaLogs();
        }
        if(!timerIsStart){
            timerIsStart = true;
            startHeartBeat();
        }
    }

    public synchronized void addLog(Throwable ex){
        if(!isSaveLog || ex == null) return;
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        addLog(null,result);

        //错误日志要及时保存，连续来几条错误日志的话间隔不超过200ms就到最后一条的时候再保存
        heartBeatHandler.removeCallbacks(heartBeatRunnable);
        heartBeatHandler.postDelayed(heartBeatRunnable, 200);
    }

    private synchronized void savaLogs(){
        if(logList == null || logList.size() == 0) return;
        StringBuilder sb = new StringBuilder();
        for(String str: logList){
            sb.append(str);
        }
        logList.clear();
        ThreadPoolManager.getCacheInstance().execute(() -> {
            try {
                addTxtToFile(sb.toString());
            }catch (Exception e){
                e.printStackTrace();
            }
        });
    }


    /**
     * 重新开始计时
     */
    private void startHeartBeat(){
        heartBeatHandler.removeCallbacks(heartBeatRunnable);
        heartBeatHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);
    }

    public String getApiFileName(){
        if(apiFileName == null){
            String todayDate = getTodayDate();
            SharedPreferences sharedPreferences = SPUtils.getPreferences(CommonLibary.getContext());
            if(!todayDate.equals(sharedPreferences.getString("dataTime",""))){
                sharedPreferences.edit().putInt("logVersion",0).apply();
                sharedPreferences.edit().putString("dataTime",todayDate).apply();
            }
            int logVersion = sharedPreferences.getInt("logVersion",0);
            return String.format(logFileName,todayDate,logVersion);
        }else {
            return apiFileName;
        }
    }

    // 将字符串写入到文本文件中
    private synchronized void addTxtToFile(String logContent) {
        //生成文件夹之后，再生成文件，不然会出错
        if(CommonLibary.getContext() == null) return;
        if(TextUtils.isEmpty(logPath)){
            logPath = getLogPath(mContext);
        }
        String todayDate = getTodayDate();
        SharedPreferences sharedPreferences = SPUtils.getPreferences(CommonLibary.getContext());
        if(!todayDate.equals(sharedPreferences.getString("dataTime",""))){
            sharedPreferences.edit().putInt("logVersion",0).apply();
            sharedPreferences.edit().putString("dataTime",todayDate).apply();
        }
        int logVersion = sharedPreferences.getInt("logVersion",0);
        apiFileName = String.format(logFileName,todayDate,logVersion);
        makeFilePath(logPath, apiFileName);

        String strFilePath = logPath + apiFileName;
        try {
            File file = new File(strFilePath);
            if(FileUtils.getFileSize(file) > 1048576){
                //如果大于1M，则重新新建文件存入
                file = new File(logPath + String.format(logFileName,todayDate,++logVersion));
                sharedPreferences.edit().putInt("logVersion",logVersion).apply();
            }
            if (!file.exists()) {
                LogUtils.d("LogManager", "Create the file:" + strFilePath);
                file.getParentFile().mkdirs();
                file.createNewFile();
            }
            BufferedRandomAccessFile raf = new BufferedRandomAccessFile(file, "rwd");
            raf.seek(file.length());
            raf.write(logContent.getBytes());
            raf.close();
        } catch (Exception e) {
            LogUtils.e("LogManager", "Error on write File:" + e);
        }
    }

//    /**
//     * 使用BufferedWriter进行文本内容的追加
//     * @param file
//     * @param content
//     */
//    private void addTxtToFileBuffered(File file, String content) {
//        //在文本文本中追加内容
//        BufferedWriter out = null;
//        try {
//            //FileOutputStream(file, true),第二个参数为true是追加内容，false是覆盖
//            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
//            out.newLine();//换行
//            out.write(content);
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            try {
//                if (out != null) {
//                    out.close();
//                }
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }
//    }

    // 生成文件
    private File makeFilePath(String filePath, String fileName) {
        File file = null;
        try {
            file = new File(filePath);
            if (!file.exists()) {
                file.mkdirs();
            }
            file = new File(filePath + fileName);
            if (!file.exists()) {
                file.createNewFile();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 取消定时并保存日志到文件
     */
    public void save(){
        if(isSaveLog){
            timerIsStart = false;
            heartBeatHandler.removeCallbacks(heartBeatRunnable);
            savaLogs();
        }
    }

    public String getLogDirectory() {
        return logPath;
    }

    public void D(@Nullable String tag, @NonNull String msg){
        LogUtils.d(tag, msg);
        addLog(tag, msg);
    }

    public void E(@Nullable String tag, @NonNull String msg){
        LogUtils.d(tag, msg);
        addLog(tag, msg);
    }
}
